﻿#ifndef UPLUGINMANAGER_H
#define UPLUGINMANAGER_H

#include "../utkcore_global.h"

#include <QObject>
#include <QReadWriteLock>

class UPluginInfo;
class UPluginManagerPrivate;
class UTKCORE_EXPORT UPluginManager : public QObject
{
    Q_OBJECT
public:
    static UPluginManager* instance();

    void addObject(QObject* obj);
    void removeObject(QObject* obj);
    QList<QObject*> allObjects();
    QList<UPluginInfo*> allPlugins();
    QReadWriteLock* listLock();
    template <typename T> QList<T*> getObjects()
    {
        QReadLocker lock(listLock());
        QList<T*> results;
        QList<QObject*> all = allObjects();
        foreach (QObject* obj, all)
        {
            T* result = qobject_cast<T*>(obj);
            if (result)
            {
                results += result;
            }
        }
        return results;
    }

    template <typename T, typename Predicate>
    QList<T*> getObjects(Predicate predicate)
    {
        QReadLocker lock(listLock());
        QList<T*> results;
        QList<QObject*> all = allObjects();
        foreach (QObject* obj, all)
        {
            T* result = qobject_cast<T*>(obj);
            if (result && predicate(result))
            {
                results += result;
            }
        }
        return results;
    }
    template <typename T> T* getObject()
    {
        QReadLocker lock(listLock());
        QList<QObject*> all = allObjects();
        foreach (QObject* obj, all)
        {
            if (T* result = qobject_cast<T*>(obj))
            {
                return result;
            }
        }
        return nullptr;
    }
    template <typename T, typename Predicate> T* getObject(Predicate predicate)
    {
        QReadLocker lock(listLock());
        QList<QObject*> all = allObjects();

        foreach (QObject* obj, all)
        {
            if (T* result = qobject_cast<T*>(obj))
            {
                if (predicate(result))
                {
                    return result;
                }
            }
        }
        return nullptr;
    }

    QObject* getObjectByName(const QString &name);
    QObject* getObjectByClassName(const QString &className);

    void loadPlugins();
    QStringList pluginPaths();
    void setPluginPaths(const QStringList &paths);
    bool hasError();

    bool isInitializationDone();

    void shutdown();

signals:
    void objectAdded(QObject* obj);
    void aboutToRemoveObject(QObject* obj);

    void pluginsChanged();
    void initializationDone();

private:
    explicit UPluginManager(QObject* parent = nullptr);
    ~UPluginManager();

    Q_DISABLE_COPY(UPluginManager)
    QScopedPointer<UPluginManagerPrivate> d_ptr;
    Q_DECLARE_PRIVATE(UPluginManager)
};

#endif // UPLUGINMANAGER_H
